/*
 * Copyright (c) 2017-2021, Lindenis Tech. Ltd.
 * All rights reserved.
 *
 * File:
 *
 * Description: reliable data transfer
 *
 * Author:
 *      xiaoshujun@lindeni.com
 *
 * Create Date:
 *      2021/03/22
 *
 * History:
 *
 */

#ifndef __LIND_TUNNEL_H__
#define __LIND_TUNNEL_H__

#ifdef __cplusplus
extern "C" {
#endif

#include "osal_types.h"

/**
 * uid: user id
 * sid: session id
 * gid: channel group id
 * cid: channel id
 */

#define MAX_UID_LEN         32
#define MAX_PSW_LEN         32

#define LDT_MTU             1400
#define LDT_PKG_MAX_SIZE    1400
#define LDT_PKG_HEAD_SIZE   4

#define MAX_SESSION_DEVICE  16
#define MAX_SESSION_CLIENT  64

#define LDT_ZCFG_ADDR       (NULL)//"127.0.0.1"

typedef struct _LDT_VideoFrame_t
{
    uint32_t    codec_id;       // refer to LDT_CODEC_ID_e
    uint32_t    format;         // refer to LDT_FRAME_FORMAT_e
    uint32_t    timestamp;      // ms
} LDT_VFrame_t, LDT_AFrame_t;

typedef enum _LDT_MSG_e
{
    LDT_MSG_UNKNOWN                 = 0,

    /*
     * callback after logined the server.
     */
    LDT_MSG_LOGIN                   = 1,

    /*
     * callback after logout the server, maybe net error.
     */
    LDT_MSG_LOGOUT                  = 2,

    /*
     * callback only for Device APP.
     * Device APP may ignore this message or do what it wants.
     */
    LDT_MSG_SESSION_CONNECT         = 3,

    /*
     * callback this message while net error.
     * Client APP should invoke 'LDT_Cli_DisconnDevice()'.
     * Device APP may ignore this msg or do what it wants.
     */
    LDT_MSG_SESSION_DISCONN         = 4,

    /*
     * [Asynchronous] callback after channel group created.
     * Client/Device APP create a thread to listen channel group ioctrls.
     */
    LDT_MSG_CHANNEL_GROUP_CREATED   = 5,

    /*
     * [!!! Synchronous] callback before channel group destroy.
     * Client/Device APP should clean resource created itself, and return as soon as possible.
     */
    LDT_MSG_CHANNEL_GROUP_DESTORY   = 6,

    /*
     * callback the data bitrate(bps) received.
     */
    LDT_MSG_SESSION_BITRATE         = 7,
} LDT_MSG_e;

typedef enum _LDT_IOCTRL_FORMAT_e
{
    LDT_IOCTRL_FORMAT_RAW           = 0,
    LDT_IOCTRL_FORMAT_JSON          = 1,
} LDT_IOCTRL_FORMAT_e;

typedef struct _LDT_Config_t
{
    int32_t (* on_event)(void * user, LDT_MSG_e msg, void * ext1, void * ext2);
    void *      user;

    /*
     * set the max number of connections at the same time
     * for device suggest: 4, should not larger than 8
     */
    int32_t     max_session;

    /*
     * initialize invoking will return '0' when success or block until timeout
     */
    int32_t     timeout_ms;
} LDT_Cfg_t;

typedef struct _LDT_ChannelGroupConfig_t
{
    int32_t     media_rdt;                  // 0: udp, 1: rdt
    int32_t     ioctlchn_format;            // refer: LDT_IOCTRL_FORMAT_e
    int32_t     ioctlchn_cache_cnt;         // default max 25
    int32_t     vchn_cache_frame_cnt;       // default max 25
    int32_t     achn_cache_frame_cnt;       // default max 25
    int32_t     rawchn_cache_size;          // default max 128K Bytes
} LDT_ChnGrpCfg_t;

/*******************************************************************************
 * client api
 *******************************************************************************/

/**
 * The client initialize lind-tunnel library.
 * NoBlock.
 * APP should listen these messages(refer: LDT_MSG_e):
 *      LDT_MSG_LOGIN:
 *          !!! All APIs like "LDT_Cli_xxx()" should be invoke after got 'LDT_MSG_LOGIN' message.
 *      LDT_MSG_LOGOUT
 *      LDT_MSG_SESSION_DISCONN
 *      LDT_MSG_CHANNEL_GROUP_CREATED
 *      LDT_MSG_CHANNEL_GROUP_DESTORY
 *
 * @param
 *    p_cli_cfg     [in]: pass the necessary parameters
 *    lisence_key   [in]: sdk lisence key
 * @return
 *     0: success
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_Initialize(const LDT_Cfg_t * p_cfg, const char * lisence_key);

/**
 * The client cleanup lind-tunnel library resources.
 *
 * @param
 * @return
 */
void LDT_Cli_Cleanup();

/**
 * The client search for devices on line in the LAN.
 *
 * @param
 *    uid_list [in/out]: the uid list
 *    uid_list [out]: the max list count
 * @return
 *    >0: the actual uid list count
 *     0: not found
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_QueryDevice(char * uid_list[], int32_t imax);

/**
 * The client connect to device by uid and password.
 *
 * @param
 *    uid [in]: the uid of device to be connected
 *    psw [in]: the password of device to be connected
 *    timeout_ms [in] : this invoking block until timeout while try to connect the devcie
 * @return
 *   >=0: success, the session id
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_ConnDevice(const char * uid, const char * psw, int32_t timeout_ms);

/**
 * The client disconnect a session.
 *
 * @param
 *    sid [in]: the connected session id
 * @return
 */
void LDT_Cli_DisconnDevice(int sid);

/**
 * The client create an channel group.
 *
 * @param
 *    sid          [in]: the connected session id
 *    p_chngrp_cfg [in]: the channel group parameters
 * @return
 *   >=0: success, the channel group id
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_CreateChnGrp(int sid, LDT_ChnGrpCfg_t * p_chngrp_cfg);

/**
 * The client destroy an channel group.
 *
 * @param
 *    sid [in]: the connected session id
 *    gid [in]: the channel group id
 * @return
 */
void LDT_Cli_DestroyChnGrp(int sid, int gid);

/*******************************************************************************
 * device api
 *******************************************************************************/

/**
 * The device initialize lind-tunnel library.
 * NoBlock.
 * APP should listen these messages(refer: LDT_MSG_e):
 *      LDT_MSG_LOGIN
 *      LDT_MSG_LOGOUT
 *      LDT_MSG_SESSION_CONNECT
 *      LDT_MSG_SESSION_DISCONN
 *      LDT_MSG_CHANNEL_GROUP_CREATED
 *      LDT_MSG_CHANNEL_GROUP_DESTORY
 *
 * @param
 *    p_dev_cfg     [in]: pass the necessary parameters
 *    lisence_key   [in]: sdk lisence key
 *    uid           [in]: the user id of this device
 *    password      [in]: the password of this device
 * @return
 *     0: success
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Dev_Initialize(const LDT_Cfg_t * p_cfg, const char * lisence_key, const char * uid, const char * password);

/**
 * The device cleanup lind-tunnel library resources.
 *
 * @param
 * @return
 */
void LDT_Dev_Cleanup();

/*******************************************************************************
 * data send/recv api
 *******************************************************************************/

int32_t LDT_Cli_Send(int32_t sid, int32_t cid, int32_t cmd,
                       const uint8_t * data, int32_t size);

int32_t LDT_Cli_Recv(int32_t sid, int32_t cid, int32_t * cmd,
                       uint8_t * data, int32_t * size, int32_t timeout_ms);

int32_t LDT_Dev_Send(int32_t sid, int32_t cid, int32_t cmd,
                       const uint8_t * data, int32_t size);

int32_t LDT_Dev_Recv(int32_t sid, int32_t cid, int32_t * cmd,
                       uint8_t * data, int32_t * size, int32_t timeout_ms);

/**
 * The client/device send command to device/client.
 *
 * @param
 *    sid  [in]: the connected session id
 *    gid  [in]: the channel group id
 *    cmd  [in]: the command type
 *    data [in]: the append data of this command
 *    size [in]: the size of 'data'
 * @return
 *    =0: success
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_SendIOCtrl(int32_t sid, int32_t gid, int32_t cmd, const uint8_t * data, int32_t size);
int32_t LDT_Dev_SendIOCtrl(int32_t sid, int32_t gid, int32_t cmd, const uint8_t * data, int32_t size);


/**
 * The client wait response from device.
 * The client use 'LDT_Cli_SendIOCtrl()' send a ioctrl to device, and want a response from device.
 * Then it can use 'LDT_Cli_WaitResp()' to get it.
 *
 * @param
 *    sid        [in]    : the connected session id
 *    gid        [in]    : the channel group id
 *    cmd        [in]    : the wanted response command type
 *    data       [in/out]: the append data of this response
 *    size       [in/out]: the size of 'data'
 *    timeout_ms [in]    : this invoking block until timeout when there is no response
 * @return
 *    =0: success
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_WaitResp(int32_t sid, int32_t gid, int32_t cmd,
                         uint8_t * data, int32_t * size, int32_t timeout_ms);


/**
 * The client/device recv command from device/client.
 *
 * @param
 *    sid        [in]    : the connected session id
 *    gid        [in]    : the channel group id
 *    cmd        [out]   : the got command type
 *    data       [out]   : the append data of this command
 *    size       [in/out]: set the max sizeof 'data', and got the actual size
 *    timeout_ms [in]    : this invoking block until timeout when there is no data receive
 * @return
 *    =0: success
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_RecvIOCtrl(int32_t sid, int32_t gid, int32_t * cmd,
                           uint8_t * data, int32_t * size, int32_t timeout_ms);
int32_t LDT_Dev_RecvIOCtrl(int32_t sid, int32_t gid, int32_t * cmd,
                           uint8_t * data, int32_t * size, int32_t timeout_ms);

/**
 * The client/device flush received IOCtrl buffer.
 *
 * @param
 *    sid        [in]    : the connected session id
 *    gid        [in]    : the channel group id
 * @return
 */
void    LDT_Cli_FlushRecvIOCtrl(int32_t sid, int32_t gid);
void    LDT_Dev_FlushRecvIOCtrl(int32_t sid, int32_t gid);

/**
 * The client/device send video frame to device/client.
 *
 * @param
 *    sid    [in]: the connected session id
 *    gid    [in]: the channel group id
 *    format [in]: the format of video frame
 *    data   [in]: the append data of this command
 *    size   [in]: the size of 'data'
 * @return
 *    =0: success
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_SendVideo(int32_t sid, int32_t gid, const LDT_VFrame_t * format,
                          const uint8_t * data, int32_t size);
int32_t LDT_Dev_SendVideo(int32_t sid, int32_t gid, const LDT_VFrame_t * format,
                          const uint8_t * data, int32_t size);

/**
 * The client/device recv video frame from device/client.
 *
 * @param
 *    sid        [in]    : the connected session id
 *    gid        [in]    : the channel group id
 *    format     [out]   : the format of video frame
 *    data       [out]   : the append data of this command
 *    size       [in/out]: set the max size of 'data', and got the actual size
 *    timeout_ms [in]    : this invoking block until timeout when there is no data receive
 * @return
 *    =0: success
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_RecvVideo(int32_t sid, int32_t gid, LDT_VFrame_t * format,
                          uint8_t * data, int32_t * size, int32_t timeout_ms);
int32_t LDT_Dev_RecvVideo(int32_t sid, int32_t gid, LDT_VFrame_t * format,
                          uint8_t * data, int32_t * size, int32_t timeout_ms);

/**
 * The client/device flush received video buffer.
 *
 * @param
 *    sid        [in]    : the connected session id
 *    gid        [in]    : the channel group id
 * @return
 */
void    LDT_Cli_FlushRecvVideo(int32_t sid, int32_t gid);
void    LDT_Dev_FlushRecvVideo(int32_t sid, int32_t gid);

/**
 * The client/device send audio frame to device/client.
 *
 * @param
 *    sid    [in]: the connected session id
 *    gid    [in]: the channel group id
 *    format [in]: the format of audio frame
 *    data   [in]: the append data of this command
 *    size   [in]: the size of 'data'
 * @return
 *    =0: success
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_SendAudio(int32_t sid, int32_t gid, const LDT_AFrame_t * format,
                          const uint8_t * data, int32_t size);
int32_t LDT_Dev_SendAudio(int32_t sid, int32_t gid, const LDT_AFrame_t * format,
                          const uint8_t * data, int32_t size);

/**
 * The client/device recv audio frame from device/client.
 *
 * @param
 *    sid        [in]    : the connected session id
 *    gid        [in]    : the channel group id
 *    format     [out]   : the format of audio frame
 *    data       [out]   : the append data of this command
 *    size       [in/out]: set the max size of 'data', and got the actual size
 *    timeout_ms [in]    : this invoking block until timeout when there is no data receive
 * @return
 *    =0: success
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_RecvAudio(int32_t sid, int32_t gid, LDT_AFrame_t * format,
                          uint8_t * data, int32_t * size, int32_t timeout_ms);
int32_t LDT_Dev_RecvAudio(int32_t sid, int32_t gid, LDT_AFrame_t * format,
                          uint8_t * data, int32_t * size, int32_t timeout_ms);

/**
 * The client/device flush received audio buffer.
 *
 * @param
 *    sid        [in]    : the connected session id
 *    gid        [in]    : the channel group id
 * @return
 */
void    LDT_Cli_FlushRecvAudio(int32_t sid, int32_t gid);
void    LDT_Dev_FlushRecvAudio(int32_t sid, int32_t gid);

/**
 * The client/device send raw data to device/client.
 *
 * @param
 *    sid    [in]: the connected session id
 *    gid    [in]: the channel group id
 *    data   [in]: the append data of this command
 *    size   [in]: the size of 'data'
 * @return
 *    =0: success
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_SendRaw(int32_t sid, int32_t gid,
                        const uint8_t * data, int32_t size);
int32_t LDT_Dev_SendRaw(int32_t sid, int32_t gid,
                        const uint8_t * data, int32_t size);

/**
 * The client/device recv raw data from device/client.
 *
 * @param
 *    sid        [in]    : the connected session id
 *    gid        [in]    : the channel group id
 *    data       [out]   : the append data of this command
 *    size       [in/out]: set the max size of 'data', and got the actual size
 *    timeout_ms [in]    : this invoking block until timeout when there is no data receive
 * @return
 *    =0: success
 *    <0: error, refer to LDT_ERROR_E
 */
int32_t LDT_Cli_RecvRaw(int32_t sid, int32_t gid,
                        uint8_t * data, int32_t * size, int32_t timeout_ms);
int32_t LDT_Dev_RecvRaw(int32_t sid, int32_t gid,
                        uint8_t * data, int32_t * size, int32_t timeout_ms);

/**
 * The client/device flush received raw buffer.
 *
 * @param
 *    sid        [in]    : the connected session id
 *    gid        [in]    : the channel group id
 * @return
 */
void    LDT_Cli_FlushRecvRaw(int32_t sid, int32_t gid);
void    LDT_Dev_FlushRecvRaw(int32_t sid, int32_t gid);


/**
 * LDT Server APIs
 */
typedef enum
{
    LDT_SRV_MSG_NONE            = 0,
    LDT_SRV_MSG_DEVICE_IN       = 0x1,
    LDT_SRV_MSG_DEVICE_OUT      = 0x2,
} LDT_SRV_MSG_e;

/**
 * The app integrate ldt server.
 * For example: the app wants to listen to the login/logout status of devices.
 *
 * @param
 *    ip        [in]: the ip address of ldt server.
 *                    if set to 'NULL', use local default ip address.
 *                    default priority: hg0 > eth0 > wlan0 > lo
 *    port      [in]: the port of ldt server.
 *                    if set to '0', use a port requested from system.
 *    on_event  [in]: callback function got message('LDT_SRV_MSG_e') from ldt server.
 *    user      [in]: the handler of callback function 'on_event'.
 * @return
 *    =0: success
 *    <0: error, refer to LDT_ERROR_E
 */
int LDT_Srv_Initialize(char * ip, const short port, comm_msg_cb on_event, char * user);

/**
 * Stop the ldt server.
 *
 * @param
 * @return
 */
void LDT_Srv_Cleanup();

#ifdef __cplusplus
}
#endif

#endif  // __LIND_TUNNEL_H__

